<template>
  <div class="file-converter-container">
    <div class="file-converter">
      <div class="card">
        <div class="card-header">
          <h2>文件转换工具</h2>
        </div>

        <div class="upload-section">
          <div
            class="file-upload"
            @drop.prevent="handleDrop"
            @dragover.prevent="handleDragOver"
            @dragleave.prevent="handleDragLeave"
          >
            <input type="file" id="file" @change="handleFileChange" class="file-input" />
            <label for="file" class="upload-button" ref="uploadLabel">选择文件</label>
            <div class="drag-tip">或将文件拖到此处</div>
            <div class="file-name" v-if="selectedFile">已选择: {{ selectedFile.name }}</div>
          </div>
        </div>

        <div class="download-section" v-if="selectedFile">
          <div class="progress-container" v-if="progress > 0">
            <div class="progress-bar">
              <div class="progress" :style="{ width: `${progress}%` }"></div>
            </div>
            <div class="progress-text">
              <div>进度: {{ progress }}%</div>
              <div>已处理数据: {{ formatSize(processedSize) }}</div>
              <div>总数据: {{ formatSize(totalSize) }}</div>
              <div>已处理行数: {{ processedLines }}</div>
            </div>
          </div>
          <button class="download-btn" @click="handleConvert" :disabled="isConverting">
            {{ isConverting ? '转换中...' : '开始转换' }}
          </button>
        </div>
      </div>
    </div>
  </div>
</template>

<script setup lang="ts">
import { ref } from 'vue'
import { ElMessage } from 'element-plus'
import JSONbig from 'json-bigint'

const selectedFile = ref<File | null>(null)
const isConverting = ref(false)
const progress = ref(0)
const processedSize = ref(0)
const totalSize = ref(0)
const processedLines = ref(0)
const storeName = 'jsonData'

// 格式化文件大小
const formatSize = (bytes: number) => {
  if (bytes === 0) return '0 B'
  const k = 1024
  const sizes = ['B', 'KB', 'MB', 'GB']
  const i = Math.floor(Math.log(bytes) / Math.log(k))
  return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]
}

// 初始化 IndexedDB
const initDB = () => {
  return new Promise<IDBDatabase>((resolve, reject) => {
    const request = indexedDB.open('jsonConverterDB', 1)

    request.onerror = () => reject(request.error)
    request.onsuccess = () => resolve(request.result)

    request.onupgradeneeded = (event) => {
      const db = (event.target as IDBOpenDBRequest).result
      if (!db.objectStoreNames.contains(storeName)) {
        db.createObjectStore(storeName, { keyPath: 'id', autoIncrement: true })
      }
    }
  })
}

// 存储数据到 IndexedDB
const storeData = async (data: string) => {
  const db = await initDB()
  return new Promise<void>((resolve, reject) => {
    const transaction = db.transaction([storeName], 'readwrite')
    const store = transaction.objectStore(storeName)
    const request = store.add({ data })

    request.onsuccess = () => resolve()
    request.onerror = () => reject(request.error)
  })
}

// 清空数据库
const clearDB = async () => {
  const db = await initDB()
  return new Promise<void>((resolve, reject) => {
    const transaction = db.transaction([storeName], 'readwrite')
    const store = transaction.objectStore(storeName)
    const request = store.clear()

    request.onsuccess = () => resolve()
    request.onerror = () => reject(request.error)
  })
}

// 分批下载数据
const downloadChunked = async (filename: string) => {
  const db = await initDB()
  const tx = db.transaction(storeName, 'readonly')
  const store = tx.objectStore(storeName)
  const countRequest = store.count()

  const totalCount = await new Promise<number>((resolve) => {
    countRequest.onsuccess = () => resolve(countRequest.result)
  })

  let downloadedCount = 0
  const allData: string[] = []

  const cursorRequest = store.openCursor()
  cursorRequest.onsuccess = async (event) => {
    const cursor = (event.target as IDBRequest).result
    if (cursor) {
      allData.push(cursor.value.data)
      downloadedCount++
      progress.value = Math.min(100, Math.round((downloadedCount / totalCount) * 100))
      cursor.continue()
    } else {
      if (allData.length > 0) {
        const blob = new Blob(allData, { type: 'application/json' })
        const url = URL.createObjectURL(blob)
        const a = document.createElement('a')
        a.href = url
        a.download = `${filename}_converted.json`
        document.body.appendChild(a)
        a.click()
        document.body.removeChild(a)
        URL.revokeObjectURL(url)
      }
      await clearDB()
      ElMessage.success('转换完成')
    }
  }
}

const handleFileChange = (event: Event) => {
  const input = event.target as HTMLInputElement
  const file = input.files?.[0]
  if (!file) return
  selectedFile.value = file
  totalSize.value = file.size
}

const handleConvert = async () => {
  if (!selectedFile.value) return
  isConverting.value = true
  progress.value = 0
  processedSize.value = 0
  processedLines.value = 0

  try {
    const file = selectedFile.value
    const chunkSize = 1024 * 1024 // 1MB chunks
    const totalChunks = Math.ceil(file.size / chunkSize)
    let currentChunk = 0
    let buffer = ''
    let isFirstLine = true

    // 清空数据库
    await clearDB()

    const reader = new FileReader()

    reader.onload = async (e) => {
      if (e.target?.result) {
        buffer += e.target.result as string
        const lines = buffer.split('\n')
        buffer = lines.pop() || '' // 保留最后一个不完整的行

        for (let i = 0; i < lines.length; i++) {
          const line = lines[i].trim()
          if (!line) continue

          try {
            const jsonObj = JSONbig.parse(line)
            const jsonStr = JSON.stringify(jsonObj, (key, value) => {
              if (typeof value === 'string' && /^\d{15,}$/.test(value)) {
                return value
              }
              return value
            })
            if (!isFirstLine) {
              await storeData(',\n')
              processedSize.value += 2
            } else {
              await storeData('[')
              processedSize.value += 1
              isFirstLine = false
            }
            // 存储处理后的数据
            await storeData(jsonStr)
            processedSize.value += jsonStr.length
            processedLines.value++
          } catch (e) {
            console.error(`解析JSON失败: ${line}`, e)
          }
        }

        currentChunk++
        progress.value = Math.min(100, Math.round((currentChunk / totalChunks) * 100))

        if (currentChunk < totalChunks) {
          readNextChunk()
        } else {
          // 处理最后一个不完整的行
          if (buffer.trim()) {
            try {
              const jsonObj = JSONbig.parse(buffer.trim())
              const jsonStr = JSON.stringify(jsonObj, (key, value) => {
                if (typeof value === 'string' && /^\d{15,}$/.test(value)) {
                  return value
                }
                return value
              })

              if (!isFirstLine) {
                await storeData(',\n')
                processedSize.value += 2
              } else {
                await storeData('[')
                processedSize.value += 1
              }
              await storeData(jsonStr)
              processedSize.value += jsonStr.length
              processedLines.value++
            } catch (e) {
              console.error(`解析JSON失败: ${buffer.trim()}`, e)
            }
          }

          // 添加数组结束标记
          await storeData(']')
          processedSize.value += 1

          // 分批下载数据
          await downloadChunked(file.name.replace(/\.[^/.]+$/, ''))

          // 重置状态
          selectedFile.value = null
          const input = document.getElementById('file') as HTMLInputElement
          if (input) {
            input.value = ''
          }
          progress.value = 0
          processedSize.value = 0
          totalSize.value = 0
          processedLines.value = 0
        }
      }
    }

    const readNextChunk = () => {
      const start = currentChunk * chunkSize
      const end = Math.min(start + chunkSize, file.size)
      const chunk = file.slice(start, end)
      reader.readAsText(chunk)
    }

    readNextChunk()
  } catch (error) {
    console.error('转换失败:', error)
    ElMessage.error(error instanceof Error ? error.message : '转换失败，请重试')
  } finally {
    isConverting.value = false
  }
}

// 处理拖拽进入
const handleDragOver = (event: DragEvent) => {
  event.target && (event.target as HTMLElement).closest('.file-upload')?.classList.add('dragover')
}

// 处理拖拽离开
const handleDragLeave = (event: DragEvent) => {
  event.target &&
    (event.target as HTMLElement).closest('.file-upload')?.classList.remove('dragover')
}

// 处理文件拖放
const handleDrop = (event: DragEvent) => {
  event.target &&
    (event.target as HTMLElement).closest('.file-upload')?.classList.remove('dragover')
  const file = event.dataTransfer?.files[0]

  if (!file) return

  // 创建一个新的 FileList 对象
  const dataTransfer = new DataTransfer()
  dataTransfer.items.add(file)

  // 更新 input 的文件
  const fileInput = document.getElementById('file') as HTMLInputElement
  if (fileInput) {
    fileInput.files = dataTransfer.files
    handleFileChange({ target: { files: [file] } } as unknown as Event)
  }
}
</script>

<style scoped>
.file-converter-container {
  width: 100%;
  height: 100vh;
  overflow-y: auto;
  background-color: #f5f7f8;
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
}

.file-converter {
  padding: 20px;
  max-width: 1200px;
  margin: 0 auto;
  font-family:
    -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
}

.card {
  background: white;
  border-radius: 12px;
  box-shadow: 0 4px 24px rgba(0, 0, 0, 0.06);
  padding: 24px;
  margin-bottom: 20px;
}

.card-header {
  margin-bottom: 24px;
}

.card-header h2 {
  margin: 0;
  font-size: 20px;
  color: #2d3748;
  font-weight: 600;
}

.upload-section {
  width: 100%;
  margin: 20px 0;
}

.file-upload {
  position: relative;
  display: block;
  padding: 30px;
  border: 2px dashed #e2e8f0;
  border-radius: 8px;
  background: #f8fafc;
  transition: all 0.3s ease;
  text-align: center;
  width: 100%;
  margin: 0 auto;
}

.file-upload.dragover {
  border-color: #4f46e5;
  background: #eef2ff;
}

.file-input {
  position: absolute;
  width: 0;
  height: 0;
  opacity: 0;
}

.upload-button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 15px 30px;
  background: #4f46e5;
  color: white;
  border-radius: 8px;
  cursor: pointer;
  transition: all 0.3s ease;
  font-weight: 500;
  width: 80%;
  max-width: 400px;
  font-size: 16px;
}

.upload-button:hover {
  background: #4338ca;
  transform: translateY(-1px);
  box-shadow: 0 4px 12px rgba(79, 70, 229, 0.2);
}

.drag-tip {
  margin-top: 10px;
  color: #64748b;
  font-size: 14px;
}

.file-name {
  margin-top: 15px;
  color: #4f46e5;
  font-size: 14px;
  font-weight: 500;
}

.download-section {
  margin-top: 24px;
  text-align: center;
}

.download-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 15px 30px;
  background: #4f46e5;
  color: white;
  border: none;
  border-radius: 8px;
  cursor: pointer;
  transition: all 0.3s ease;
  font-size: 16px;
  font-weight: 500;
  width: 80%;
  max-width: 400px;
}

.download-btn:hover:not(:disabled) {
  background: #4338ca;
  transform: translateY(-1px);
  box-shadow: 0 4px 12px rgba(79, 70, 229, 0.2);
}

.download-btn:disabled {
  background: #94a3b8;
  cursor: not-allowed;
}

.progress-container {
  margin-bottom: 20px;
  width: 80%;
  max-width: 400px;
  margin-left: auto;
  margin-right: auto;
}

.progress-bar {
  width: 100%;
  height: 8px;
  background-color: #e2e8f0;
  border-radius: 4px;
  overflow: hidden;
}

.progress {
  height: 100%;
  background-color: #4f46e5;
  transition: width 0.3s ease;
}

.progress-text {
  text-align: center;
  margin-top: 8px;
  color: #64748b;
  font-size: 14px;
  display: flex;
  flex-direction: column;
  gap: 4px;
}
</style>
